#ifndef FUNDAMENTAL_LIB_SMART_POINTER_H
#define FUNDAMENTAL_LIB_SMART_POINTER_H

namespace Sherry
{
	namespace Fundamentals
	{
		template<typename T>
		class SmartPointer
		{
			template<typename T1>
			friend class SmartPointer;
		private:
			T * pointer;
			int * refCount;
		public:
			SmartPointer()
			{
				pointer = 0;
				refCount = 0;
			}
			SmartPointer(T * ptr)
				: pointer(0), refCount(0)
			{
				this->operator=(ptr);
			}
			SmartPointer(const SmartPointer<T> & ptr)
				: pointer(0), refCount(0)
			{
				this->operator=(	ptr);
			}
			SmartPointer(SmartPointer<T> && str)
				: pointer(0), refCount(0)
			{
				this->operator=(static_cast<SmartPointer<T> &&>(str));
			}
			SmartPointer<T>& operator=(T * ptr)
			{
				Dereferance();

				pointer = ptr;
				if(ptr)
				{
					refCount = new int;
					(*refCount) = 1;
				}
				else
					refCount = 0;
				return *this;
			}
			SmartPointer<T>& operator=(const SmartPointer<T> & ptr)
			{
				if(ptr.pointer != pointer)
				{
					Dereferance();
					pointer = ptr.pointer;
					refCount = ptr.refCount;
					(*refCount)++;
				}
				return *this;
			}

			template<typename T1>
			SmartPointer(const SmartPointer<T1> & ptr)
				: pointer(0), refCount(0)
			{
				this->operator=(ptr);
			}
			template<typename T1>
			SmartPointer<T> & operator = (const SmartPointer<T1> & ptr)
			{
				if(ptr.pointer != pointer)
				{
					Dereferance();
					pointer = dynamic_cast<T*>(ptr.pointer);
					refCount = ptr.refCount;
					(*refCount)++;
				}
				return *this;
			}

			SmartPointer<T>& operator=(SmartPointer<T> && ptr)
			{
				if(ptr.pointer != pointer)
				{
					Dereferance();
					pointer = ptr.pointer;
					refCount = ptr.refCount;
					ptr.pointer = 0;
					ptr.refCount = 0;
				}
				return *this;
			}
			~SmartPointer()
			{
				Dereferance();
			}

			void Dereferance()
			{
				if(pointer)
				{
					if((*refCount) > 1)
					{
						(*refCount)--;
					}
					else
					{
						delete pointer;
						delete refCount;
					}
				}
			}
			T * operator->()
			{
				return pointer;
			}
			T * Ptr()
			{
				return pointer;
			}
			// operator TargetType() { body }
			operator bool()
			{
				return pointer != 0;
			}
		};
	}
}

#endif